package com.panopset.gui.container;

import static com.panopset.Util.dspmsg;
import static com.panopset.Util.getCanonicalPath;
import static com.panopset.Util.log;
import static com.panopset.Util.x;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;

import com.panopset.UpdateListener;
import com.panopset.gui.TButton;
import com.panopset.gui.TPanel;
import com.panopset.gui.TTextField;
import com.panopset.java.Version;
import com.panopset.swing.FieldGui;
import java.util.ArrayList;

/**
 * Super class of PanFile and PanDirectory.
 *
 * @author Karl Dinwiddie
 *
 */
public abstract class AbstractPanFile extends PanField<JPanel> {
    /**
     *
     * @param borderTitle
     *            Border title.
     * @param toolTipText
     *            Tool tip text.
     */
    public AbstractPanFile(final String borderTitle, final String toolTipText) {
        this(borderTitle, toolTipText, "");
    }


    /**
     *
     * @param borderTitle
     *            Border title.
     * @param toolTipText
     *            Tool tip text.
     * @param defaultValue
     *            Default value.
     */
    public AbstractPanFile(final String borderTitle, final String toolTipText,
            final String defaultValue) {
        super(borderTitle, toolTipText, defaultValue);
        getPanel().setLayout(new GridLayout(1, 1));
        setFieldGui(new FieldGui() {

            @Override
            public String getValue() {
                return getFileTextField().getText();
            }

            @Override
            public void setValue(final String value) {
                getFileTextField().setText(value);
            }
        });
    }

    /**
     * Description.
     */
    private String description = "";

    /**
     * Update listeners.
     */
    private final List<UpdateListener> updateListeners
            = new ArrayList<UpdateListener>();

    /**
     * Add update listener.
     *
     * @param updateListener
     *            Update listener.
     */
    public final void addUpdateListener(final UpdateListener updateListener) {
        updateListeners.add(updateListener);
    }

    /**
     * Update all listeners.
     */
    private void update() {
        for (UpdateListener updateListener : updateListeners) {
            updateListener.update();
        }
    }

    /**
     * Set selected file.
     *
     * @param file
     *            Selected file.
     */
    public final void setSelectedFile(final File file) {
        if (file != null && file.exists()) {
            getFileTextField().setText(getCanonicalPath(file));
            update();
        }
    }

    /**
     * Get file.
     *
     * @return File.
     */
    public final File getFile() {
        return new File(getValue());
    }

    /**
     * Create a file or directory path.
     */
    protected abstract void create();

    /**
     * Create new file.
     */
    final void doNew() {
        setSelectedFile(fileDialog(JFileChooser.SAVE_DIALOG));
        File f = getFile();
        if (f == null || "".equals(getCanonicalPath(f))) {
            dspmsg(x("No file selected."));
            return;
        }
        create();
    }

    /**
     * @param dialogType
     *            JFileChooser Dialog type.
     * @return Selected file.
     */
    private File fileDialog(final int dialogType) {
        File rtn = null;
        File dftPath = new File(getCanonicalPath(new File("x")))
                .getParentFile();
        JFileChooser chooser = new JFileChooser();
        if ("".equals(getFileTextField().getText().trim())) {
            if ((dftPath != null) && (getCanonicalPath(dftPath).length() > 0)) {
                chooser.setSelectedFile(dftPath);
            }
        } else {
            chooser.setSelectedFile(getFile());
        }
        chooser.setFileSelectionMode(getFileSelectionMode());
        chooser.setDialogTitle(getPrompt());
        chooser.setDialogType(dialogType);
        if (chooser.showOpenDialog(new JFrame())
                 == JFileChooser.APPROVE_OPTION) {
            rtn = chooser.getSelectedFile();
        }
        if (rtn != null) {
            getFileTextField().setText(getCanonicalPath(rtn));
        }
        update();
        return rtn;
    }

    /**
     * @return JFileDialog selection mode.
     */
    protected abstract int getFileSelectionMode();

    /**
     * @return PanFile panel.
     */
    public final JPanel createGui() {
        return getPanel();
    }

    /**
     * Create component.
     */
    private TPanel panel;

    @Override
    public final TPanel createComponent() {
        String requiredPrompt = null;

        requiredPrompt = createPrompter() + " " + x("required");

        panel = new TPanel.Builder().tip(x("Select") + " " + description)
                .layout(new GridBagLayout()).requiredPrompt(requiredPrompt)
                .construct();

        final GridBagConstraints gbc = new GridBagConstraints();

        TButton btn = new TButton(x("Browse"), KeyEvent.VK_B, x("Select")
                + " " + description, new ActionListener() {

            public void actionPerformed(final ActionEvent e) {
                doNew();
            }
        });
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 0;
        panel.add(btn, gbc);
        gbc.gridx++;
        panel.add(getDropper(), gbc);
        gbc.gridx++;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        panel.add(getFileTextField(), gbc);
        return panel;
    }

    /**
     * dropper.
     */
    private TTextField dropper;

    /**
     * @return Dropper.
     */
    private TTextField getDropper() {
        if (dropper == null) {
            dropper = new TTextField(2);
            dropper.setEditable(false);
            dropper.setText(">>");
            dropper.setToolTipText(x("Drag 'n drop file here."));
            dropper.setBackground(Color.CYAN);
            dropper.setDragEnabled(true);
            dropper.setTransferHandler(new FileDropHandler());
        }
        return dropper;
    }

    /**
     * File text field.
     */
    private TTextField fileTextField;

    /**
     * @return File text field.
     */
    public TTextField getFileTextField() {
        if (fileTextField == null) {
            fileTextField = new TTextField(FILENAME_WIDTH);
        }
        return fileTextField;
    }

    /**
     * 30.
     */
    private static final int FILENAME_WIDTH = 30;

    /**
     * File drop handler.
     *
     * Reference:
     *
     * http://weblogs.java.net/blog/shan_man/archive/2006/09/toplevel_drop_w.
     * html
     *
     */
    final class FileDropHandler extends TransferHandler {

        /**
         * Can import.
         *
         * @param supp
         *            Transfer support.
         * @return True if the type of drag and drop is a list of files.
         */
        public boolean canImport(final TransferSupport supp) {
            /* for the demo, we'll only support drops (not clipboard paste) */
            if (!supp.isDrop()) {
                return false;
            }

            /* return true if and only if the drop contains a list of files */
            return supp.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
        }

        /**
         * Import data.
         *
         * @param supp
         *            Transfer support.
         * @return True if successful.
         */
        public boolean importData(final TransferSupport supp) {
            if (!canImport(supp)) {
                return false;
            }

            /* fetch the Transferable */
            Transferable t = supp.getTransferable();

            try {
                Object data = t.getTransferData(DataFlavor.javaFileListFlavor);
                if (!(data instanceof java.util.List<?>)) {
                    return false;
                }
                java.util.List<?> objList = (java.util.List<?>) data;
                for (Object o : objList) {
                    if (o instanceof File) {
                        File file = (File) o;
                        AbstractPanFile.this.setSelectedFile(file);
                        return true;
                    }
                }
            } catch (Exception e) {
                log(e);
            }

            return false;
        }

        /**
         * Serial version id.
         */
        private static final long serialVersionUID = Version.UNI_VERSION;
    }

    /**
     * Prompt.
     */
    private String prompt;

    /**
     * @return Prompt.
     */
    private String getPrompt() {
        if (prompt == null) {
            prompt = x("Please select a") + " " + createPrompter();
        }
        return prompt;
    }

    /**
     * @return Canonical path of file.
     */
    public final String getFullPath() {
        return getCanonicalPath(getFile());
    }

    /**
     * @return directory or file prompter.
     */
    protected abstract String createPrompter();
}
